package org.summerframework.component.httpclient;

import org.summerframework.core.util.CheckUtil;
import org.summerframework.core.util.UrlEncodeUtil;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.client.CookieStore;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.content.*;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.web.bind.annotation.RequestMethod;

import java.io.File;
import java.io.InputStream;
import java.util.*;

/**
 * @author shichao
 */
public class HttpParam {

    public HttpParam() {
        header.put(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36");
    }

    public HttpParam(String url) {
        this();
        setUrl(url);
    }

    public static String urlReg = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";

    /**
     * 请求地址
     */
    private String url;

    /**
     * 请求方式,暂时只支持get,post
     */
    private RequestMethod method = RequestMethod.GET;

    private CloseableHttpClient customHttpClient;

    /**
     * get请请求时获取次参数
     */
    private String param;

    private Map<String, Object> originalParams = new HashMap<>(16);

    private Map<String, List<String>> params = new HashMap<>(16);

    private Map<String, List<ContentBody>> contentBodies = new HashMap<>(16);

    /**
     * 请求头部
     */
    private Map<String, String> header = new HashMap<String, String>();

    /**
     * 所需携带参数
     */
    private CookieStore cookieStore;

    /**
     * 当为301或302时, 是否请求至200为止
     */
    private boolean flag = false;

    /**
     * 当flag为true时,判断停止地址
     */
    private String stopUrl;

    private boolean isParam = false;

    private boolean hasFile = false;

    public String charset = "UTF-8";

    /**
     * post请求时 contentBody
     *
     * @return
     */
    public HttpEntity entity;

    public HttpEntity getEntity() {
        return entity;
    }

    public void setEntity(HttpEntity entity) {
        this.entity = entity;
    }

    public Map<String, List<ContentBody>> getContentBodies() {
        return contentBodies;
    }

    public boolean hasFile() {
        return hasFile;
    }

    public Map<String, Object> getOriginalParams() {
        return originalParams;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    /**
     * url是否含有参数
     *
     * @author 石超
     * @date 2015-4-14 下午2:06:03
     */
    private void setIsParam() {
        if (url.split("[?]").length > 1) {
            isParam = true;
        } else {
            isParam = false;
        }
    }

    public boolean isParam() {
        return isParam;
    }

    /**
     * 获取完整地址
     *
     * @return
     * @author 石超
     * @date 2015-4-14 下午2:06:23
     */
    public String getUri() {
        String url = getUrl();
        String hash = "";
        if (StringUtils.isBlank(url)) {
            return null;
        }

        StringBuffer sb = new StringBuffer();
        if (url.contains("#")) {
            int index = url.indexOf("#");
            hash = url.substring(index);
            url = url.substring(0, index);
        }
        sb.append(url);

        // 列表参数
        for (Map.Entry<String, List<String>> param : params.entrySet()) {
            if (!StringUtils.contains(sb.toString(), "?")) {
                sb.append("?");
            }

            for (Object obj : param.getValue()) {
                if (obj.getClass().isPrimitive() || obj instanceof String) {
                    if (!sb.toString().endsWith("?")) {
                        sb.append("&");
                    }

                    sb.append(param.getKey()).append("=").append(UrlEncodeUtil.encodeURL(String.valueOf(obj)));
                }
            }
        }

        sb.append(hash);
        return sb.toString();
    }

    public void addParam(String key, Object value) {
        if (value == null) {
            return;
        }

        if (isFormBody(value)) {
            addContentBodyParam(key, value);
        } else {
            addParam(key, value.toString());
        }
    }

    public void addParam(String key, String value) {
        add(params, key, value);
    }

    public void addParam(Map<String, ?> param) {
        if (MapUtils.isNotEmpty(param)) {
            for (String key : param.keySet()) {
                addParam(key, param.get(key));
            }
        }
    }

    /**
     * 支持 file、bety[]、inputstream、String
     * <p>
     * String的编码格式默认为 ContentType.TEXT_PLAIN
     *
     * @param key
     * @param value
     * @p
     */
    public void addContentBodyParam(String key, Object value) {
        ContentBody contentBody = null;

        if (value instanceof File) {
            contentBody = new FileBody((File) value);
            hasFile = true;
        } else if (value instanceof byte[]) {
            byte[] byteVlue = (byte[]) value;
            contentBody = new ByteArrayBody(byteVlue, key);
            hasFile = true;
        } else if (value instanceof String) {
            String str = (String) value;
            contentBody = new StringBody(str, ContentType.TEXT_PLAIN);
            hasFile = true;
        } else if (value instanceof InputStream) {
            InputStream in = (InputStream) value;
            contentBody = new InputStreamBody(in, key);
        } else {
            return;
        }

        add(contentBodies, key, contentBody);
    }

    <T> void add(Map<String, List<T>> map, String key, T value) {
        List<T> list = map.get(key);

        if (list == null) {
            list = new ArrayList<>();
        }

        list.add(value);
        map.put(key, list);
    }


    public void addListParam(String key, List<String> list) {
        if (params.get(key) != null) {
            params.get(key).addAll(list);
        } else {
            params.put(key, list);
        }
    }

    public void addListParam(String key, String... list) {
        if (params.get(key) != null) {
            params.get(key).addAll(Arrays.asList(list));
        } else {
            params.put(key, Arrays.asList(list));
        }
    }

    /**
     * 检验是否正确的url地址
     *
     * @return
     * @author 石超
     * @date 2015-4-14 下午2:06:36
     */
    public static boolean isUrl(String url) {
        return CheckUtil.isUrl(url);
    }

    public void resetUrl(String url) {
        setUrl(url);
        param = null;
        originalParams = new HashMap<>(16);
        contentBodies = new HashMap<>(16);
    }

    public RequestMethod getMethod() {
        return method;
    }

    public void setMethod(RequestMethod method) {
        this.method = method;
    }

    public String getParam() {
        return param;
    }

    public void setParam(String param) {
        this.param = param;

        if (StringUtils.isNotBlank(param)) {
            if (param.startsWith("?")) {
                param = param.substring(1);
            }

            String[] paramList = param.split("&");
            for (String str : paramList) {
                String[] map = str.split("=");
                if (map.length >= 2) {
                    addParam(map[0], map[1]);
                }
            }
        }
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        if (!HttpParam.isUrl(url)) {
            return;
        }

        if (url.contains("?")) {
            String[] urls = url.split("[?]");
            String[] params = urls[1].split("&");
            Arrays.stream(params).forEach(param -> {
                String[] values = param.split("=");
                addParam(values[0], values[1]);
            });

            this.url = urls[0];
        } else {
            this.url = url;
        }

        setIsParam();
    }

    public Map<String, List<String>> getParams() {
        return params;
    }

    boolean isFormBody(Object obj) {
        return obj instanceof File || obj instanceof byte[] || obj instanceof InputStream;
    }

    public void setParams(Map<String, Object> params) {
        if (params != null) {
            for (Map.Entry<String, Object> param : params.entrySet()) {
                if (isFormBody(param.getValue())) {
                    addContentBodyParam(param.getKey(), param.getValue());
                } else if (param.getValue() instanceof List) {
                    List list = (List) param.getValue();
                    for (Object obj : list) {
                        if (isFormBody(obj)) {
                            addContentBodyParam(param.getKey(), obj);
                        } else {
                            addParam(param.getKey(), obj);
                        }
                    }
                } else {
                    if (param.getValue() != null) {
                        addParam(param.getKey(), param.getValue().toString());
                    } else {
                        addParam(param.getKey(), "");
                    }
                }
            }
        }
    }

    public Map<String, String> getHeader() {
        return header;
    }

    public void setHeader(Map<String, String> header) {
        this.header = header;
    }

    public void addHeader(String key, String value) {
        this.header.put(key, value);
    }

    public CookieStore getCookieStore() {
        return cookieStore;
    }

    public void setCookieStore(CookieStore cookieStore) {
        this.cookieStore = cookieStore;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public String getStopUrl() {
        return stopUrl;
    }

    public void setStopUrl(String stopUrl) {
        this.stopUrl = stopUrl;
    }

    public CloseableHttpClient getCustomHttpClient() {
        return customHttpClient;
    }

    public void setCustomHttpClient(CloseableHttpClient customHttpClient) {
        this.customHttpClient = customHttpClient;
    }
}
